package cn.wolfcode.service.impl;

import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.CodeMsg;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.domain.OrderInfo;
import cn.wolfcode.domain.Product;
import cn.wolfcode.domain.SeckillProduct;
import cn.wolfcode.domain.SeckillProductVo;
import cn.wolfcode.feign.ProductFeignApi;
import cn.wolfcode.mapper.OrderInfoMapper;
import cn.wolfcode.mapper.SeckillProductMapper;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by lanxw
 */
@Service
public class SeckillProductServiceImpl implements ISeckillProductService {
    @Autowired
    private SeckillProductMapper seckillProductMapper;
    @Resource
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private ProductFeignApi productFeignApi;

    @Override
    public List<SeckillProductVo> queryByTime(Integer time) {
        List<SeckillProduct> seckillProducts = seckillProductMapper.queryCurrentlySeckillProduct(time);
        if (seckillProducts == null || seckillProducts.size() == 0) {
            return Collections.EMPTY_LIST;
        }
        List<Long> productIds = seckillProducts.stream().map(item -> {
            return item.getProductId();
        }).collect(Collectors.toList());

        // 开始远程调用，从另一个库里进行连表
        Result<List<Product>> productFeignResult = productFeignApi.queryByIds(productIds);
        if (productFeignResult == null || productFeignResult.hasError()) {
            throw new BusinessException(SeckillCodeMsg.PRODUCT_SERVER_ERROR);
        }
        Map<Long, Product> productMap = new HashMap<>();
        productFeignResult.getData().stream().forEach(item -> {
            productMap.put(item.getId(), item);
        });

        // 桑品和秒杀数据集合，封装vo返回
        return seckillProducts.stream().map(item -> {
            Product product = productMap.getOrDefault(item.getProductId(), new Product());
            SeckillProductVo seckillProductVo = new SeckillProductVo();
            BeanUtils.copyProperties(product, seckillProductVo);
            seckillProductVo.setCurrentCount(item.getStockCount());
            BeanUtils.copyProperties(item, seckillProductVo);
            return seckillProductVo;
        }).collect(Collectors.toList());

    }

    /**
     *  从缓存中把数据拿出来
     * @param time
     * @return
     */
    @Override
    public List<SeckillProductVo> queryByTimeFromCache(Integer time) {
        String key = SeckillRedisKey.SECKILL_PRODUCT_HASH.getRealKey(time+"");
        List<Object> values = redisTemplate.opsForHash().values(key);
        List<SeckillProductVo> ret = new ArrayList<>();
        for (Object value : values) {
            ret.add(JSON.parseObject((String)value,SeckillProductVo.class));
        }
        return ret;
    }

    @Override
    public SeckillProductVo find(Integer time, Long seckillId) {
        SeckillProduct seckillProduct = seckillProductMapper.find(seckillId);
        List<Long> productIds = new ArrayList<>();
        productIds.add(seckillProduct.getProductId());
        Result<List<Product>> listResult = productFeignApi.queryByIds(productIds);
        if(listResult == null || listResult.hasError()){
            throw new BusinessException(SeckillCodeMsg.PRODUCT_SERVER_ERROR);
        }
        Product product = listResult.getData().get(0);
        SeckillProductVo seckillProductVo = new SeckillProductVo();
        BeanUtils.copyProperties(product,seckillProductVo);
        BeanUtils.copyProperties(seckillProduct,seckillProductVo);
        return seckillProductVo;
    }

    @Override
    public boolean decrstockCount(Long seckillId) {
        return seckillProductMapper.decrStock(seckillId) > 0;
    }

    @Override
    public void syncStockToRedis(Integer time, Long seckillId) {
        SeckillProduct seckillProduct = seckillProductMapper.find(seckillId);
        if(seckillProduct.getStockCount() > 0){
            String key = SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.getRealKey(String.valueOf(time));
            //把数据库中的库存同步到 redis中
            redisTemplate.opsForHash().put(key,seckillId,seckillProduct.getStockCount());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void findProductByNo(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.find(orderNo);
        if(OrderInfo.STATUS_ARREARAGE.equals(orderInfo.getStatus())){
            //修改订单状态
            int affectRows = orderInfoMapper.updateCancelStatus(orderNo, OrderInfo.STATUS_TIMEOUT);
            if(affectRows == 0){
                // 代表用户已经支付
                return;
            }
            //真实库存回补
            this.incrStockCount(orderInfo.getSeckillId());
            //预库存回补
            this.syncStockToRedis(orderInfo.getSeckillTime(), orderInfo.getSeckillId());
        }
    }

    @Override
    public void incrStockCount(Long seckillId) {
        seckillProductMapper.incrStock(seckillId);
    }
}
